package com.kj.tms.common.utils.calx.sheet;


import com.kj.tms.common.bean.dto.ExternalRelationDto;
import com.kj.tms.common.bean.dto.WithinRelationDto;
import com.kj.tms.common.utils.StringUtil;
import com.kj.tms.common.utils.calx.Calx;
import com.kj.tms.common.utils.calx.Cell;
import com.kj.tms.common.utils.calx.util.Parser;
import com.kj.tms.common.utils.calx.util.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @ProjectName: ledger
 * @Package: com.hongtu.tms.ledger.calx.factory.sheet
 * @ClassName: Sheet
 * @Description: [calx sheet页处理类]
 * @Author: [zhuyongjie]
 * @CreateDate: 2018/4/16 11:18
 * @UpdateUser: []
 * @UpdateDate: 2018/4/16 11:18
 * @UpdateRemark: []
 * @Version: 1.0
 * Copyright: 北京宏图天安 Copyright (c) 2018
 */
public class Sheet {
    private static Logger logger = LoggerFactory.getLogger(Sheet.class);
    /**
     * sheet唯一标识
     */
    private String identifier;
    /**
     * sheet数据列表
     */
    private List<Map<String,Object>> dataList;
    /**
     * 表内列表
     */
    private List<WithinRelationDto> winList;
    /**
     * 表间列表
     */
    private List<ExternalRelationDto> extList;
    /**
     * 单元格列表
     */
    private Map<String, Cell> cells;
    /**
     * 变量
     */
    private Map<String,Object> variables;
    /**
     * 配置项
     */
    private Map<String,Object> config;
    /**
     * sheet中单元格数量
     */
    private Integer counter = 1;
    /**
     * sheet页是否和其他sheet页相关联标识
     */
    private boolean relatedSheet = false;
    /**
     * sheet页id
     */
    private String elementId;
    /**
     * 包含依赖于此页的表的注册表
     */
    private List<Object> dependant;

    /**
     *包含此sheet依赖的的注册表
     */
    private List<Object> dependencies;
    /**
     * 标识当前sheet页的计算完成与否
     */
    private boolean calculated = false;
    /**
     * 标识当前sheet是否正则计算中
     */
    private boolean calculating = false;
    /**
     * 当前正则计算的单元格对象
     */
    private Cell activeCell = null;
    private Integer totalCell = 0;
    private List<String> affectedCell;

    /**
     * 编译器
     */
    private Parser parser;
    /**
     * 金额单元格匹配规则AMTA,AMTB
     */
//    private final String cellRegx = "^(AMT)[A-Za-z]+";
    private final String cellRegx = "^AMT[A-Z]+$";
    /**
     * 中括弧匹配正则,替换([或者])
     */
    //private String parenthesesRegex = "(\\[)+|(\\])+"
    private String parenthesesRegex = "(\\(\\[)+|(\\]\\))+";
    /**
     * Calx对象
     */
    private Calx calx;

    public Sheet(String identifier, List<Map<String, Object>> dataList,
                 List<WithinRelationDto> winList,
                 List<ExternalRelationDto> extList,
                 String formCode, Map<String,Object> config, Calx calx) {
        this.identifier = identifier;
        this.dataList = dataList;
        this.winList = winList;
        this.extList = extList;
        this.elementId = formCode;
        this.calx = calx;
        initConf();
        if(!StringUtil.isEmptyOrNull(config)){
            this.config.putAll(config);
        }
        this.init();
    }

    /**
     * @Method      initConf
     * @Param
     * @Return      void
     * @Exception
     * @Description [初始化默认配置]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 9:41
     */
    private void initConf() {
        DefaultConfig defaultConfig = new DefaultConfig();
        this.config = defaultConfig.getConfig();
    }//initConf end

    public Sheet() {
        initConf();
        this.init();
    }

    /**
     * @Method      init
     * @Param
     * @Return      void
     * @Exception
     * @Description [sheet页初始化]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 9:46
     */
    private void init() {
//        System.out.println("sheet#[" + this.identifier + "] 正在初始化...");
//        logger.info("sheet#[" + this.identifier + "] 正在初始化...");
        //计算金额单元格数量
        if(!StringUtil.isEmptyOrNull(this.dataList)
                && this.dataList.size() > 0){

            Map<String, Object> dataMap = dataList.get(0);
            for(Map.Entry<String,Object> entry : dataMap.entrySet()){
                String key = entry.getKey();
                boolean matches = Pattern.matches(this.cellRegx, key);
                if(matches){
                    this.totalCell += dataList.size();
                }
            }
//            System.out.println("sheet#[" + this.identifier + "]" + "包含单元格数量 : "+ this.totalCell);
//            logger.info("sheet#[" + this.identifier + "]" + "包含单元格数量 : "+ this.totalCell);
        }
        //将数据装换为单元格形式
        List<Cell> cells = initCell();
//        System.out.println("已初始化单元格数量 : " + cells.size());
        //初始化解析器
        this.parser = new Parser(this);
        this.cells = new HashMap<String,Cell>();
        this.affectedCell = new ArrayList<String>();
        //注册单元格
        if(!StringUtil.isEmptyOrNull(cells)
                && cells.size() > 0){
            for(Cell cell : cells){
                this.registerCell(cell);
            }
        }
//        System.out.println("sheet#[" + this.identifier + "]" + "已注册单元格数量 : " + this.affectedCell.size());
        logger.info("sheet#[" + this.identifier + "]" + "已注册单元格数量 : " + this.affectedCell.size());
    }//sheet.init() end

    /**
     * @Method      registerCell
     * @Param		cell
     * @Return      void
     * @Exception
     * @Description [注册单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 14:20
     */
    private void registerCell(Cell cell) {
//        System.out.println("sheet#[" + this.identifier + "]!Cell#[" + cell.getAddress() + "]正在初始化...");
//        logger.info("sheet#[" + this.identifier + "]!Cell#[" + cell.getAddress() + "]正在初始化...");
        String address = cell.getAddress();
        this.cells.put(address,cell);

        if(!this.affectedCell.contains(cell.getAddress())){
            this.affectedCell.add(cell.getAddress());
        }
    }//sheet.registerCell() end

    /**
     * @Method      initCell
     * @Param
     * @Return      java.util.List<com.hongtu.tms.ledger.calx.factory.cell.Cell>
     * @Exception
     * @Description [将台账金额数据转换为单元格形式]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 11:03
     */
    private List<Cell> initCell() {
        List<Cell> result = new ArrayList<>();
        if(!StringUtil.isEmptyOrNull(this.dataList)
                && this.dataList.size() > 0){
            for(Map<String,Object> data : dataList){
                //循环数据
                for(Map.Entry<String,Object> entry : data.entrySet()){
                    //循环每一行，每一个字段
                    String field = entry.getKey();
                    String fieldValue = StringUtil.toString(entry.getValue());
                    boolean matches = Pattern.matches(cellRegx, field);
                    if(matches){
                        //金额单元格
                        String rowno = StringUtil.toString(data.get("ROWNO"));
                        String dataCell = field + rowno;
                        String cellCode = field + "_" + rowno;
                        //查找公式设置
//                        System.out.println("data-cell : " + dataCell + ";\tcellCode : " + cellCode);
                        //计算公式
                        String formula = findFormula(cellCode);
                        Cell cell = new Cell(dataCell, formula, "", this, fieldValue,field,rowno);
                        result.add(cell);
                    }//if end
                }//for end
            }//for end
        }//if end
        return result;
    }//sheet.initCell() end

    /**
     * @Method      findFormula
     * @Param		cellCode
     * @Return      java.lang.String
     * @Exception
     * @Description [查找单元格计算公式]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 14:03
     */
    private String findFormula(String cellCode) {

        String formula = "";
        //查找表内
        if(!StringUtil.isEmptyOrNull(this.winList)
                && this.winList.size() > 0){
            for(WithinRelationDto dto : this.winList){
                if(dto.getCellCode().equals(cellCode)){
                    formula = dto.getCalcExp().replaceAll(parenthesesRegex,"").replaceAll("_","").toUpperCase();
                    break;//跳出循环
                }
            }//for end
        }//if end
        //查找表间
        if(!StringUtil.isEmptyOrNull(this.extList)
                && this.extList.size() > 0){
            for(ExternalRelationDto dto : this.extList){
                if(dto.getCellCode().equals(cellCode)){
                    formula = dto.getCalcExp().replaceAll(parenthesesRegex,"").replaceAll("_","");
                    break;//跳出循环
                }
            }//for end
        }//if end
        return formula;
    }//findFormula() end


    /**
     * @Method      getCellRangeValue
     * @Param		addressStart
     * @Param		addressStop
     * @Return      java.util.Map<java.lang.String,java.lang.Object>
     * @Exception
     * @Description [获取A1:B8单元格范围内每一个单元格的值]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/16 11:27
     */
    public Map<String,String> getCellRangeValue(String addressStart,
                                                String addressStop)
            throws Exception {
        Map<String,String> cellRangeValue = new HashMap<String,String>();
        try {
            addressStart = addressStart.toUpperCase();
            addressStop = addressStop.toLowerCase();
//            List<String> cellRangeAddress = Utility.cellRange(addressStart, addressStop);
            List<String> cellRangeAddress = Utility.cellRange(addressStart, addressStop,this);
            if(!StringUtil.isEmptyOrNull(cellRangeAddress) && cellRangeAddress.size() > 0){
                for(String cellAddreess :cellRangeAddress){
                    //得到cell值
                    cellRangeValue.put(cellAddreess,this.getCellValue(cellAddreess));
                }//for end
            }//if end
        }catch (Exception e){
            throw e;
        }//try end
        return cellRangeValue;
    }//sheet.getCellRangeValue() end

    /**
     * @Method      getCellValue
     * @Param		address
     * @Return      java.lang.String
     * @Exception
     * @Description [获取单元格值]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 11:37
     */
    public String getCellValue(String address) {
        String result = "";
        String cell = address.toUpperCase();
        if(!this.cells.containsKey(cell)){
            return result;
        }
        return  this.cells.get(cell).getValue();
    }//sheet.getCellValue() end

    /**
     * @Method      buildCellDependency
     * @Param
     * @Return      void
     * @Exception
     * @Description [建立单元格依赖关系]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 15:38
     */
    public void buildCellDependency() throws Exception {
        if(!StringUtil.isEmptyOrNull(this.cells)){
            for(Map.Entry<String,Cell> entry : this.cells.entrySet()){
                Cell cell = entry.getValue();
                cell.buildDependency();
            }
        }
    }//sheet.buildCellDependency() end

    /**
     * @Method      getCell
     * @Param		address
     * @Return      com.hongtu.tms.ledger.calx.factory.cell.Cell
     * @Exception
     * @Description [根据单元格地址获取单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 17:20
     */
    public Cell getCell(String address) {

       address = address.toUpperCase();
       if(!StringUtil.isEmptyOrNull(this.cells.containsKey(address))){
           Cell cell = this.cells.get(address);
           return cell;
       }else{
           return null;
       }
    }//sheet.getCell() end

    /**
     * @Method      getCellRange
     * @Param		addressStart
     * @Param		addressStop
     * @Return      void
     * @Exception
     * @Description [获取给定范围的单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 9:11
     */
    public Map<String,Cell> getCellRange(String addressStart, String addressStop) throws Exception {
        Map<String,Cell> cellRange = new HashMap<String,Cell>();
        addressStart = addressStart.toUpperCase();
        addressStop = addressStop.toUpperCase();
//        List<String> cellList = Utility.cellRange(addressStart, addressStop);
        List<String> cellList = Utility.cellRange(addressStart, addressStop,this);
        for(String address : cellList){
            cellRange.put(address,this.getCell(address));
        }
        return cellRange;
    }//sheet.getCellRange end

    /**
     * @Method      getRemoteCell
     * @Param		sheetId
     * @Param		address
     * @Return      void
     * @Exception
     * @Description [获取表间单元格：sheet!A1]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 9:25
     */
    public Cell getRemoteCell(String sheetId, String address) {
        if(!this.calx.getSheetRegistry().containsKey(sheetId)){
            return null;
        }
        return this.calx.getSheetRegistry().get(sheetId).getCell(address);
    }//sheet.getRemoteCell end

    /**
     * @Method      calculate
     * @Param
     * @Return      void
     * @Exception
     * @Description [执行计算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 9:51
     */
    public void calculate() throws Exception {
//        System.out.println("sheet.calculate() : sheet#[" + this.identifier + "] 正在执行计算...");
        this.clearAffectedCell();
        //todo：onBeforeCalculate执行计算前执行
        //计算依赖
        this.calculateDependency(this.identifier);
        //设置所有有公式单元格
        this.clearProcessedFlag();
        for(Map.Entry<String,Cell> entry: this.cells.entrySet()){
            Cell cell = entry.getValue();
            if(!StringUtil.isEmptyOrNull(cell)){
                cell.processDependency();
            }
        }//for end

        this.setCalculated(true);
        //todo:表间计算重新计算

        //todo:onAfterCalculate

        //todo:onBeforeRender

//        this.renderComputedValue();
        //todo:onAfterRender

    }//sheet.calculate() end

    /**
     * @Method      clearProcessedFlag
     * @Param
     * @Return      void
     * @Exception
     * @Description [设置单元格处理状态]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 10:04
     */
    private void clearProcessedFlag() {
       for(Map.Entry<String,Cell> entry : this.cells.entrySet()){
           Cell cell = entry.getValue();
           if(!StringUtil.isEmptyOrNull(cell.getDataFormula())){
               cell.setProcessed(false);
               cell.setAffected(true);
           }else{
               cell.setProcessed(true);
               cell.setAffected(false);
           }//if end
       }//for end
    }//clearProcessedFlag() end

    /**
     * @Method      calculateDependency
     * @Param		identifier
     * @Return      void
     * @Exception
     * @Description [计算依赖]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 10:02
     */
    private void calculateDependency(String identifier) {
    }

    /**
     * @Method      clearAffectedCell
     * @Param
     * @Return      void
     * @Exception
     * @Description [清空有效单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 9:57
     */
    private void clearAffectedCell() {
        this.affectedCell.clear();
    }//clearAffectedCell end

    /**
     * @Method      evaluate
     * @Param		formula
     * @Return      java.lang.String
     * @Exception
     * @Description [计算给定的公式]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 11:22
     */
    public String evaluate(String formula) throws Exception {
        return this.parser.parse(formula);
    }//sheet.evaluate end

    /**
     * @Method      getVariable
     * @Param		string
     * @Return      java.lang.Object
     * @Exception
     * @Description [得到变量]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 11:46
     */
    public Object getVariable(String string) {
        //todo:
        return null;
    }

    /**
     * @Method      renderComputedValue
     * @Param
     * @Return      void
     * @Exception
     * @Description [更新最终结果值]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 14:44
     */
    public void renderComputedValue() {
        if(!StringUtil.isEmptyOrNull(this.affectedCell)){
            for(String address : this.affectedCell){
                this.cells.get(address).renderComputedValue();
            }//for end
            this.clearAffectedCell();
        }//if end
    }//renderComputedValue() end

    /**
     * @Method      buildChangeDependency
     * @Param		address
     * @Return      void
     * @Exception
     * @Description [获取单元格改变受影响单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 17:39
     */
    public void buildChangeDependency(String address) {
        if(this.cells.containsKey(address)){
            this.cells.get(address).buildChangeDependency();
        }
    }//buildChangeDependency end


    /**
     * @Method      checkCircularReference
     * @Param
     * @Return      java.lang.Boolean
     * @Exception
     * @Description [循环依赖检查]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/2 10:35
     */
    public Map<String,Object> checkCircularReference() {
        Map<String,Object> result = new HashMap<String,Object>();
        boolean isCircular = false;
        result.put("isCircular",isCircular);
        result.put("cell", null);
        if(!StringUtil.isEmptyOrNull(this.cells)){
            for(Map.Entry<String,Cell> entry : this.cells.entrySet()){
                Cell cell = entry.getValue();
                isCircular = cell.checkCircularReference(null);
                if(isCircular){
                    result.put("isCircular",isCircular);
                    result.put("cell", cell);
                    return result;
                }//if end
            }//for end
        }//if end
        return result;
    }//checkCircularReference end

    /**
     * @Method      removeCell
     * @Param		address
     * @Return      com.hongtu.tms.ledger.calx.factory.cell.Cell
     * @Exception
     * @Description [移除单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:29
     */
    public Cell removeCell(String address){
        if(!StringUtil.isEmptyOrNull(address)
                && this.cells.size() > 0){
            if(this.cells.containsKey(address)){
                return this.cells.remove(address);
            }//if end
        }//if end
        return null;
    }//removeCell end

    public Integer getCounter() {
        return counter;
    }

    public void setCounter(Integer counter) {
        this.counter = counter;
    }

    public Calx getCalx() {
        return calx;
    }

    public Map<String, Object> getConfig() {
        return config;
    }

    public String getIdentifier() {
        return identifier;
    }

    public List<String> getAffectedCell() {
        return affectedCell;
    }

    public Cell getActiveCell() {
        return activeCell;
    }

    public void setActiveCell(Cell activeCell) {
        this.activeCell = activeCell;
    }

    public List<Map<String, Object>> getDataList() {
        return dataList;
    }

    /**
     * @Method      setCalculated
     * @Param		calculated
     * @Return      void
     * @Exception
     * @Description [设置是否计算完毕]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 15:24
     */
    public void setCalculated(Boolean calculated) {
        if(StringUtil.isEmptyOrNull(calculated)){
            calculated = true;
        }
        this.calculated = calculated;
    }//sheet.setCalculated end


}
